package menu;

import game.Configurations;
import game.Controls;
import static game.Controls.Key.GO_DOWN;
import static game.Controls.Key.GO_UP;
import static game.Controls.Key.QA_0;
import static game.Controls.Key.QA_1;
import static game.Controls.Key.QA_2;
import static game.Controls.Key.QA_3;
import static game.Controls.Key.SEL_DOWN;
import static game.Controls.Key.SEL_LEFT;
import static game.Controls.Key.SEL_RIGHT;
import static game.Controls.Key.SEL_UP;
import static game.Controls.Key.ZOOM_IN;
import static game.Controls.Key.ZOOM_OUT;
import game.FontLoader;
import game.LScript;
import static game.TextManager.t;
import images.ImageInfo;
import images.ItemLoader;
import images.MenuLoader;
import images.ShipLoader;
import java.awt.Color;
import java.util.ArrayList;
import math.Point;
import saves.EntidadeNave;
import saves.Inventory;
import saves.Inventory.ItemEx;
import saves.Inventory.ItemInv;
import saves.SaveManager.Profile;
import saves.items.Equipamento;
import saves.items.Equipamento.LocalEquip;
import saves.items.Item;
import saves.objects.Container;
import screen.CaixaTexto.TipoInput;
import screen.Dialog;

/**
 *
 * @author Luan
 */
public class ItemInterface implements Menu {

    private ArrayList<ItemEx>[] itensAtuais;
    private ArrayList<TransItem> transaction;
    private double transactionMoney;
    private boolean lockedTransaction;
    
    private EntidadeNave player;
    //either one of these can be the other inventory holder
    private EntidadeNave npc;
    private Container container;
    //if neither, the player is only looking at his inventory (mode must be 0)
    
    private Menu parent;
    private Profile profile;
    /*
     * It can be [todo rethink]:
     * 0 - Viewing (one screen) / setting prices
     * 1 - Buying
     * 2 - Selling
     * 3 - Exchange (container/disabled ship)
     * 4 - Exchange (commerce)
     */
    private int mode;
    private int currentSide, option, options;
    private int[] selItem, selType, firstPos;
    private boolean showCommerce;
    private Dialog dialog;
    private Point.Inteiro pi0, pi1, po;
    private int w, h;
    public static final int LINE_HEIGHT = 20, ICON_SIZE = 16, QA_ICON_SIZE = 32, BIG_ICON_SIZE = 48, OPTIONS_MENU_WIDTH = 175, OWNER_HEIGHT = 70;
    private static final int k = 20 + MenuLoader.getFiExtraHeight();

    public ItemInterface() { //for pre-creation of menus only
        this(null, 0);
        this.itensAtuais = new ArrayList[1];
        this.itensAtuais[0] = new ArrayList<>();
    }

    public ItemInterface(EntidadeNave player) {
        this(player, 0);

        this.itensAtuais = new ArrayList[1];
        this.itensAtuais[0] = new ArrayList<>();
        this.gerarItensAtuais(0);
    }

    public ItemInterface(EntidadeNave player, Container c) {
        this(player, 3);

        this.container = c;
        this.itensAtuais = new ArrayList[2];
        for (int i = 0; i < this.itensAtuais.length; i++) {
            this.itensAtuais[i] = new ArrayList<>();
            this.gerarItensAtuais(i);
        }
    }

    public ItemInterface(EntidadeNave player, EntidadeNave npc, int mode) {
        this(player, mode);

        this.npc = npc;
        this.itensAtuais = new ArrayList[2];
        for (int i = 0; i < this.itensAtuais.length; i++) {
            this.itensAtuais[i] = new ArrayList<>();
            this.gerarItensAtuais(i);
        }
        
        if (mode == 1) //buying
            currentSide = 1; //starts at the other side
    }
    
    public ItemInterface(EntidadeNave player, EntidadeNave npc, ArrayList<TransItem> transaction, double transactionMoney) {
        this(player, 4);
        this.npc = npc;
        
        this.transaction = transaction;
        this.transactionMoney = transactionMoney;
        this.lockedTransaction = true;
        this.ensureTransaction();
        
        this.itensAtuais = new ArrayList[2];
        for (int i = 0; i < this.itensAtuais.length; i++) {
            this.itensAtuais[i] = new ArrayList<>();
            this.gerarItensAtuais(i);
        }
    }
    
    private boolean ensureTransaction() {
        for (int i = 0; i < this.transaction.size(); i++)
            if (!player.getInventory().removeItem(transaction.get(i).getItem(), transaction.get(i).getQtd()))
                return false;
        return true;
    }

    private ItemInterface(EntidadeNave player, int mode) {
        this.player = player;
        this.mode = mode;

        this.selItem = new int[]{0, 0};
        this.selType = new int[]{0, 0};
        this.firstPos = new int[]{0, 0};
        this.currentSide = this.option = this.options = 0;
        this.showCommerce = mode == 1 || mode == 2 || mode == 4; //default show commerce if in monetary transaction; can be toggled in most other cases
        this.dialog = null;
        this.transaction = new ArrayList<>();
        this.transactionMoney = 0;
        this.lockedTransaction = false;

        if (mode == 0 || mode == 2) {
            this.w = Configurations.SCREEN_WIDTH - 30 - OPTIONS_MENU_WIDTH;
            this.h = Configurations.SCREEN_HEIGHT - 20;
            this.pi0 = new Point.Inteiro(10, 10);
            this.po = new Point.Inteiro(20 + w, 10 + OWNER_HEIGHT);
        } else {
            this.w = Configurations.SCREEN_WIDTH - 30 - OPTIONS_MENU_WIDTH;
            this.h = (Configurations.SCREEN_HEIGHT - 30) / 2;
            this.pi0 = new Point.Inteiro(10, 10);
            this.po = new Point.Inteiro(20 + w, 10 + OWNER_HEIGHT);
            this.pi1 = new Point.Inteiro(10, h + 20);
        }
    }

    private void gerarItensAtuais(int side) {
        itensAtuais[side].clear();
        if (selType[side] == t().ITEM_CATEGORIES.length + 1) { //equips
            EntidadeNave n = side == 0 ? player : npc;
            for (int i = 0; i < LocalEquip.size(); i++)
                if (n.getEquip(i) != null)
                    itensAtuais[side].add(new ItemEx(n.getEquip(i), 1, true, 0));
        } else {
            ArrayList<ItemEx> inv;
            if (side == 0)
                inv = player.getInventory().getItems();
            else if (npc != null)
                inv = npc.getInventory().getItems();
            else
                inv = container.getInventory().getItems();
            
            for (int i = 0; i < inv.size(); i++)
                if (selType[side] >= t().ITEM_CATEGORIES.length || inv.get(i).getItem().getTipo() == selType[side]) //all items || item belongs to selected type
                    if (!inv.get(i).isLocked() || (side == 0 && mode == 0))
                        itensAtuais[side].add(inv.get(i));
        }
    }
    
    public static class TransItem extends ItemInv {
        private boolean up;

        public TransItem(Item item, int qtd, boolean up) {
            super(item, qtd);
            this.up = up;
        }

        public boolean isUp() {
            return up;
        }
    }

    @Override
    public Menu getParent() {
        return this.parent;
    }

    @Override
    public void setParent(Menu parent) {
        this.parent = parent;
    }

    private static int getQtosItens(int descSize, int h) {
        return (h - descSize - k - 15) / LINE_HEIGHT;
    }

    public Item getItem() {
        return getItem(currentSide);
    }

    private Item getItem(int side) {
        if (itensAtuais[side].isEmpty())
            return null;
        return itensAtuais[side].get(selItem[side]).getItem();
    }
    
    private void drawInv(int side, boolean owner, boolean up, int x, int y) {
        ArrayList<ItemEx> inv = itensAtuais[side];
        Item sel = getItem(side);

        //count the lines of the description box
        int descSize = BIG_ICON_SIZE + 20, //minium size, because of the image
                descLines = 1, //starts on one because of the name (extra line)
                charLength = FontLoader.TEXT.getWidth(" "); //the font is monospaced
        if (sel != null) {
            String[] ds = sel.getDescricao().split("\n");
            for (int i = 0; i < ds.length; i++)
                descLines += (int) Math.ceil((double) ds[i].length() * charLength / (w - BIG_ICON_SIZE - 20));
            descSize = Math.max(descSize, descLines * LINE_HEIGHT + 10);
        }
        int qtosItens = getQtosItens(descSize, h); //# of lines that will be displayed

        MenuLoader.bindTexture();
        MenuLoader.drawBack(x, y, w, h);
        //draw upper bar
        drawTypesBar(x, y + (up ? 0 : h - k), w, selType[side], side);

        //draw the selectedItem line
        MenuLoader.drawSel(x + 5, y + (up ? k + 10 : 20 + descSize) + LINE_HEIGHT * (selItem[side] - firstPos[side]), w - 10, LINE_HEIGHT);
        //draw the selectedItem description background
        MenuLoader.drawSel(x + 5, y + (up ? h - 5 - descSize : 5), w - 10, descSize);
        //draw scroll bar
        double fd = Math.min(1, (double) qtosItens / itensAtuais[side].size()), ff = (double) firstPos[side] / itensAtuais[side].size();
        int size = h - descSize - k - 20;
        MenuLoader.drawSel(x + 1, y + (up ? k + 10 : 20 + descSize) + (int) (size * ff), 3, (int) (fd * size));

        if (itensAtuais[side].isEmpty()) {
            FontLoader.TEXT.drawString(x + 10, y + (up ? k + 20 : 30 + descSize) - FontLoader.TEXT.getLineHeight() / 2, t().NO_ITEM);
            writeTypesBar(x, y + (up ? 0 : h - k), w, selType[side]);
            return;
        }

        //first lets determine what should be drawn, and in what order
        //structures do hold the data of items to be drawn later (different spritesheet, faster rendering)
        class ItemToDraw {

            Item e;
            int x, y, s;

            ItemToDraw(Item e, int x, int y, int s) {
                this.e = e;
                this.x = x;
                this.y = y;
                this.s = s;
            }
        }
        ArrayList<ItemToDraw> eqs = new ArrayList<>(), items = new ArrayList<>();
        //put itens in correct arrays and draw locked item's icon
        for (int i = firstPos[side]; i < inv.size() && i < firstPos[side] + qtosItens; i++) {
            int lh = y + LINE_HEIGHT * (i - firstPos[side]) + (up ? k + 10 : 20 + descSize);
            if (showCommerce && inv.get(i).isLocked()) //draw lock
                MenuLoader.drawLock(x + w - charLength * 2 - 15 - ICON_SIZE, lh + (LINE_HEIGHT - ICON_SIZE) / 2, ICON_SIZE);
            if (showCommerce) //draw currency symbol
                MenuLoader.drawCurrency(x + w - charLength * (2 + LScript.format(inv.get(i).getCost(), 2).length()) - 20 - (inv.get(i).isLocked() ? ICON_SIZE + 5 : 0) - ICON_SIZE, lh + 5 + (LINE_HEIGHT - ICON_SIZE) / 2, ICON_SIZE);
            if (inv.get(i).getItem().getTipo() == 1) //is equip
                eqs.add(new ItemToDraw(inv.get(i).getItem(), x + 10, lh, ICON_SIZE)); //add to the list for future drawing
            else
                items.add(new ItemToDraw(inv.get(i).getItem(), x + 10, lh, ICON_SIZE));
        }

        //draw the item's icons (except for the equips)
        ItemLoader.bindTexture();
        for (int i = 0; i < items.size(); i++)
            ItemLoader.drawItem(items.get(i).e.getIcone(), items.get(i).x, items.get(i).y, items.get(i).s);
        if (sel != null && sel.getTipo() != 1) //is not equip
            ItemLoader.drawItem(sel.getIcone(), x + 10, y + (up ? h - descSize : 5) + 5, BIG_ICON_SIZE); //add to the list for future drawing

        //now draw only the equip's icons
        ShipLoader.bindTexture();
        for (int i = 0; i < eqs.size(); i++)
            ItemLoader.drawEquip((Equipamento) eqs.get(i).e, eqs.get(i).x, eqs.get(i).y, eqs.get(i).s, 0);
        if (sel != null && sel.getTipo() == 1) //is equip
            ItemLoader.drawEquip((Equipamento) sel, x + 10, y + (up ? h - descSize : 5) + 5, BIG_ICON_SIZE); //add to the list for future drawing

        //fianlly draw the texts
        writeTypesBar(x, y + (up ? 0 : h - k), w, selType[side]);
        for (int i = firstPos[side]; i < inv.size() && i < qtosItens + firstPos[side]; i++) {
            int lh = y + (up ? k + 20 : 30 + descSize) + LINE_HEIGHT * (i - firstPos[side]) - FontLoader.TEXT.getLineHeight() / 2;
            FontLoader.TEXT.drawString(x + ICON_SIZE + 10, lh, inv.get(i).getItem().getNome());

            String t = "x" + inv.get(i).getQtd();
            FontLoader.TEXT.drawString(x + w - FontLoader.TEXT.getWidth(t) - 10, lh, t);

            if (showCommerce) {
                t = LScript.format(inv.get(i).getCost(), 2);
                FontLoader.TEXT.drawString(x + w - charLength * (2 + t.length()) - (inv.get(i).isLocked() ? ICON_SIZE + 5 : 0) - 15, lh, t);
            }
        }

        //render selected item description and data
        FontLoader.TEXT.drawString(x + BIG_ICON_SIZE + 10, y + (up ? h - 5 - descSize : 5), sel.getNome());
        String[] d = sel.getDescricao().split("\n");
        int charPerLine = (int) Math.floor((double) (w - BIG_ICON_SIZE - 10) / charLength), line = 1;
        for (int i = 0; i < d.length; i++) {
            int currentChar = 0;
            while (currentChar < d[i].length()) {
                int cb = currentChar;
                currentChar += charPerLine;
                String s = d[i].substring(cb, Math.min(currentChar, d[i].length()));
                FontLoader.TEXT.drawString(x + BIG_ICON_SIZE + 10, y + (up ? h - 5 - descSize : 5) + line * LINE_HEIGHT, s);
                line++;
            }
        }
    }

    //used only in the drawOption* methods
    private int _selPos;
    private String _desc, _name;
    
    private int drawOption(int lh, int p1, int p2) {
        return drawOption(lh, t().INV_MENUS_NAMES[p1][p2], t().INV_MENUS_DESC[p1][p2 - 1]);
    }
    
    private int drawOption(int lh, String name, String desc) {
        if (name.charAt(0) != ':') //: is the special character for transaction items, that should not be written here
            FontLoader.TEXT.drawString(po.x + 15, lh, name);
        lh += FontLoader.TEXT.getHeight() + 5;
        if (this.option == this.options) {
            this._selPos = lh;
            this._desc = desc;
            this._name = name;
        }
        this.options++;
        return lh;
    }
    
    private void drawOptions() {
        MenuLoader.bindTexture();
        int descSize = 7 * LINE_HEIGHT, hs = Configurations.SCREEN_HEIGHT - 20 - OWNER_HEIGHT * (mode == 0 ? 1 : 2);
        MenuLoader.drawSel(po.x, po.y, OPTIONS_MENU_WIDTH, hs - descSize - 10);
        MenuLoader.drawSel(po.x, po.y + hs - descSize, OPTIONS_MENU_WIDTH, descSize);
        options = 0;
        _selPos = -1;
        _desc = _name = null; //description of current item

        int lh = po.y + 5;
        if (mode != 0) {
            String t;
            if (lockedTransaction)
                t = t().INV_MENUS_NAMES[4][0];
            else
                t = t().INV_MENUS_NAMES[3][mode == 1 ? 5 : (mode == 2 ? 6 : 0)];
            FontLoader.TEXT_BIG.drawString(po.x + (OPTIONS_MENU_WIDTH - FontLoader.TEXT_BIG.getWidth(t)) / 2, lh, t); //transaction
            lh += FontLoader.TEXT_BIG.getHeight() + 5;
            
            if (!lockedTransaction && !itensAtuais[currentSide].isEmpty() && selType[currentSide] != t().ITEM_CATEGORIES.length + 1 && (mode != 1 || currentSide != 0) && (mode != 2 || currentSide != 1))
                lh = drawOption(lh, 3, 1); //add
            
            //draw each transaction's item
            for (int i = 0; i < transaction.size(); i++) {
                MenuLoader.bindTexture();
                MenuLoader.drawArrow(transaction.get(i).up ? 1 : 0, po.x + 15, lh);
                Item it = transaction.get(i).getItem();
                if (it instanceof Equipamento) {
                    ShipLoader.bindTexture();
                    ItemLoader.drawEquip((Equipamento) it, po.x + 32, lh, 16, 0);
                } else {
                    ItemLoader.bindTexture();
                    ItemLoader.drawItem(it.getIcone(), po.x + 32, lh, 16);
                }
                FontLoader.TEXT.drawString(po.x + 50, lh, "x" + transaction.get(i).getQtd());
                if (!lockedTransaction) //this only allows the player to remove the item, actually
                    lh = drawOption(lh, ":" + i, "Remove " + transaction.get(i).getQtd() + "x " + transaction.get(i).getItem().getNome() + " from transaction.");
                else
                    lh += FontLoader.TEXT.getHeight() + 5;
            }
            
            if (!lockedTransaction)
                lh = drawOption(lh, 3, 2); //add money
            if (transactionMoney != 0) {
                MenuLoader.bindTexture();
                MenuLoader.drawArrow(transactionMoney > 0 ? 1 : 0, po.x + 15, lh);
                MenuLoader.drawCurrency(po.x + 32, lh, 16);
                FontLoader.TEXT.drawString(po.x + 50, lh, LScript.format(transactionMoney, 2));
                lh += FontLoader.TEXT.getHeight() + 5;
            }
            
            if (lockedTransaction) {
                lh = drawOption(lh, 4, 1); //accept
                lh = drawOption(lh, 4, 2); //change
                lh = drawOption(lh, 4, 3); //deny
            } else if (transactionMoney != 0 || !transaction.isEmpty()) {
                lh = drawOption(lh, 3, 3); //commit
                lh = drawOption(lh, 3, 4); //cancel
            }
        }
        
        String t;
        if (!itensAtuais[currentSide].isEmpty() && (currentSide == 0 || mode == 3)) { //only if there are items, and if this options should be displayed (that is, it is yuor inventory, or free mode)
            t = t().INV_MENUS_NAMES[0][0]; //options
            FontLoader.TEXT_BIG.drawString(po.x + (OPTIONS_MENU_WIDTH - FontLoader.TEXT_BIG.getWidth(t)) / 2, lh, t);
            lh += FontLoader.TEXT_BIG.getHeight() + 5;
            
            if (currentSide == 0 || (npc != null && mode == 3 && selType[currentSide] == t().ITEM_CATEGORIES.length + 1)) //use only for your own invenotry (cS == 0) OR unequipping on disabled ships (npc in mode 3)
                lh = drawOption(lh, 0, 1); //use
            
            if (selType[currentSide] != t().ITEM_CATEGORIES.length + 1) { //not equips tab; you can't destroy or sell equiped items
                lh = drawOption(lh, 0, 2); //destroy
                
                if (mode != 3) { //mode 3 is free (containers and stuff), so no commerce options
                    t = t().INV_MENUS_NAMES[1][0]; //commerce
                    FontLoader.TEXT_BIG.drawString(po.x + (OPTIONS_MENU_WIDTH - FontLoader.TEXT_BIG.getWidth(t)) / 2, lh, t); //commerce
                    lh += FontLoader.TEXT_BIG.getHeight() + 5;
                    
                    lh = drawOption(lh, 1, showCommerce ? 2 : 1); //show/hide
                    lh = drawOption(lh, 1, itensAtuais[currentSide].get(selItem[currentSide]).isLocked() ? 4 : 3); //lock/unlock
                    lh = drawOption(lh, 1, 5); //price
                }
            }
        }
        
        if (_desc != null) { //if there is a description to show (at least one item)
            //draw item selector
            int currentChar = 0, charPerLine = (int) Math.floor((double) (OPTIONS_MENU_WIDTH - 10) / FontLoader.TEXT.getWidth(" ")), line = 0;
            while (currentChar < _desc.length()) {
                int cb = currentChar;
                currentChar += charPerLine;
                String s = _desc.substring(cb, Math.min(currentChar, _desc.length()));
                FontLoader.TEXT.drawString(po.x + 5, po.y + hs - descSize + line * LINE_HEIGHT, s);
                line++;
            }
        }
        
        MenuLoader.bindTexture();
        //draw selector
        if (_desc != null)
            MenuLoader.drawSelector(po.x + 7, _selPos - FontLoader.TEXT_BIG.getHeight()/2);
        
        int s = 32;
        //draw owner backgrounds
        MenuLoader.drawOwnerArrow(po.x + 5, 10 + s, false);
        if (mode != 0)
            MenuLoader.drawOwnerArrow(po.x + 5, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT, true);
        
        //draw owners images
        ShipLoader.bindTexture();
        ShipLoader.draw(player, po.x + 5, 10, s, 0, 0);
        if (mode != 0) {
            if (npc != null)
                ShipLoader.draw(npc, po.x + 5, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT + s, s, 0, 0);
            else
                container.draw(po.x + 5 + s/2, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT + 3*s/2, s, s);
        }
        
        //render owners data
        int ds = 10 + s;
        FontLoader.TEXT.drawString(po.x + ds, 10, player.getNome());
        FontLoader.TEXT.drawString(po.x + ds, 25, "Money: " + LScript.format(player.getInventory().getMoney()));
        FontLoader.TEXT.drawString(po.x + ds, 40, "Mass: " + LScript.format(player.getMass(), 2));
        FontLoader.TEXT.drawString(po.x + ds, 55, "Volume: " + LScript.format(player.getInventory().getVolume(), 2) + "/" + LScript.format(player.getInventory().getVolumeLimit(), 2));
        if (mode != 0) {
            FontLoader.TEXT.drawString(po.x + ds, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT, npc != null ? npc.getNome() : container.toString());
            Inventory inv = npc == null ? container.getInventory() : npc.getInventory();
            FontLoader.TEXT.drawString(po.x + ds, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT + 15, "Money: " + LScript.format(inv.getMoney()));
            FontLoader.TEXT.drawString(po.x + ds, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT + 30, "Mass: " + LScript.format(npc == null ? inv.getMass() : npc.getMass(), 2)); //if it is an npc, must add mass of equips
            FontLoader.TEXT.drawString(po.x + ds, Configurations.SCREEN_HEIGHT - OWNER_HEIGHT + 45, "Volume: " + LScript.format(inv.getVolume(), 2) + "/" + LScript.format(inv.getVolumeLimit(), 2));
        }
        
        if (mode == 0) {
            t = t().INV_MENUS_NAMES[2][0];
            FontLoader.TEXT_BIG.drawString(po.x + (OPTIONS_MENU_WIDTH - FontLoader.TEXT_BIG.getWidth(t)) / 2, lh, t);
            lh += FontLoader.TEXT_BIG.getHeight() + 5;
            int qas = 20 + 3 * QA_ICON_SIZE;
            drawQuickAccess(po.x + (OPTIONS_MENU_WIDTH - qas) / 2, lh);
        }
    }

    private void drawQuickAccess(int x, int y) {
        MenuLoader.bindTexture();
        Point.Inteiro[] pos = MenuLoader.drawQuickAccess(x, y, QA_ICON_SIZE);

        for (int i = 0; i < pos.length; i++)
            if (profile.getSave().getQuickAccess()[i] != null)
                profile.getSave().getQuickAccess()[i].draw(pos[i].x, pos[i].y, QA_ICON_SIZE, profile.getSave().getPlayer());
    }

    private void drawTypesBar(int x, int y, int w, int selection, int side) {
        int size = itemsLength(side) + 1;
        MenuLoader.drawInvMenu(x, y + k, w, size, selection);
        for (int i = 0; i < size; i++)
            MenuLoader.drawIcon(12 + i, x + MenuLoader.getInvPaddingX() + 35 * i, y + k - MenuLoader.getInvPaddingY() - 32, 32);
    }

    private static void writeTypesBar(int x, int y, int w, int selection) {
        String t;
        if (selection < t().ITEM_CATEGORIES.length)
            t = t().ITEM_CATEGORIES[selection];
        else if (selection == t().ITEM_CATEGORIES.length)
            t = t().ALL_ITEMS;
        else
            t = t().EQUIPS;
        FontLoader.TEXT_BIG.drawString(x + w - FontLoader.TEXT_BIG.getWidth(t) - 5, y + k - FontLoader.TEXT_BIG.getLineHeight(), t);
    }

    @Override
    public void draw() {
        drawOptions();
        drawInv(0, true, true, pi0.x, pi0.y);
        if (mode != 0 && mode != 2)
            drawInv(1, false, false, pi1.x, pi1.y);

        if (currentSide == 0)
            ImageInfo.drawColorSquare(pi0.x, pi0.y, w, h, Color.WHITE, false);
        else
            ImageInfo.drawColorSquare(pi1.x, pi1.y, w, h, Color.WHITE, false);
        if (this.dialog != null)
            this.dialog.desenhar();
    }

    private int itemsLength(int side) {
        return t().ITEM_CATEGORIES.length + ((side == 1 && container != null) ? 0 : 1);
    }

    public void moveH(boolean right) {
        if ((right && selType[currentSide] >= itemsLength(currentSide)) || (!right && selType[currentSide] <= 0))
            return;
        selItem[currentSide] = 0;
        firstPos[currentSide] = 0;
        option = 0;
        selType[currentSide] += (right ? 1 : -1);
        gerarItensAtuais(currentSide);
    }

    public void moveV(boolean down) {
        if ((down && selItem[currentSide] >= itensAtuais[currentSide].size() - 1) || (!down && selItem[currentSide] <= 0))
            return;
        selItem[currentSide] += (down ? 1 : -1);

        Item sel = itensAtuais[currentSide].get(selItem[currentSide]).getItem();
        int descSize = BIG_ICON_SIZE + 20, //minium size, because of the image
                descLines = 1, //starts on one because of the name (extra line)
                charLength = FontLoader.TEXT.getWidth(" "); //the font is monospaced
        if (sel != null) {
            String[] ds = sel.getDescricao().split("\n");
            for (int i = 0; i < ds.length; i++)
                descLines += (int) Math.ceil((double) ds[i].length() * charLength / (w - BIG_ICON_SIZE - 20));
            descSize = Math.max(descSize, descLines * LINE_HEIGHT + 10);
        }
        int qtosItens = getQtosItens(descSize, h); //# of lines that will be displayed
        if (selItem[currentSide] < firstPos[currentSide])
            firstPos[currentSide]--;
        else if (selItem[currentSide] >= firstPos[currentSide] + qtosItens)
            firstPos[currentSide]++;
    }

    public void moveCH(boolean right) {
        if (mode == 0 || mode == 2) //only one inv displaying
            return;
        if (!right && currentSide == 0)
            currentSide = 1;
        else if (right && currentSide == 1)
            currentSide = 0;
    }

    public void moveCV(boolean down) {
        if ((down && option >= options - 1) || (!down && option <= 0))
            return;
        option += (down ? 1 : -1);
    }
    
    private int validateTransaction() {
        float playerVolume = 0, npcVolume = 0;
        for (int i = 0; i < transaction.size(); i++)
            if (transaction.get(i).isUp())
                playerVolume += transaction.get(i).getItem().getVolume() * transaction.get(i).getQtd();
            else
                npcVolume += transaction.get(i).getItem().getVolume() * transaction.get(i).getQtd();
        
        if (player.getInventoryVolume() + playerVolume > player.getInventory().getVolumeLimit())
            return 1;
        if (npc != null) {
            if (npc.getInventoryVolume() + npcVolume > npc.getInventory().getVolumeLimit())
                return -1;
        } else {
            if (container.getInventory().getVolume() + npcVolume > container.getInventory().getVolumeLimit())
                return -1;
        }
        
        return 0; //everything is ok
    }

    @Override
    public Menu go() {
        if (dialog != null) {
            int code = dialog.getCodigo();
            switch (code) {
                case 0: {
                    if (dialog.getSelectedItem() == 1)
                        dialog = null;
                    else if (dialog.validate()) {
                        itensAtuais[currentSide].get(selItem[currentSide]).setCost(Double.parseDouble(dialog.getInput()));
                        dialog = null;
                    }
                    break;
                }
                case 1: {
                    if (dialog.getSelectedItem() == 1)
                        dialog = null;
                    else if (dialog.validate()) {
                        Item it = getItem();
                        int qtd = Integer.parseInt(dialog.getInput());
                        if (currentSide == 0) {
                            if (player.getInventory().removeItem(it, qtd)) {
                                if (mode != 3)
                                    transactionMoney += qtd*itensAtuais[currentSide].get(selItem[currentSide]).getCost();
                                transaction.add(new TransItem(it, qtd, false));
                                this.update();
                                dialog = null;
                            }
                        } else {
                            if (npc != null) {
                                if (npc.getInventory().removeItem(it, qtd)) {
                                    if (mode != 3)
                                        transactionMoney -= qtd*itensAtuais[currentSide].get(selItem[currentSide]).getCost();
                                    transaction.add(new TransItem(it, qtd, true));
                                    this.update();
                                    dialog = null;
                                }
                            } else {
                                if (container.getInventory().removeItem(it, qtd)) {
                                    transaction.add(new TransItem(it, qtd, true));
                                    this.update();
                                    dialog = null;
                                }
                            }
                        }
                    }
                    break;
                }
                case 2: {
                    if (dialog.getSelectedItem() == 1)
                        dialog = null;
                    else if (dialog.validate()) {
                        transactionMoney += Double.parseDouble(dialog.getInput());
                        dialog = null;
                    }
                    break;
                }
                default: //information dialog
                    dialog = null;
                    break;
            }
            return this;
        }
        if (_name == null) //do nothing
            return this;
        Item it = getItem();
        if (_name.equals(t().INV_MENUS_NAMES[0][1])) { //use
            if (it == null) //do nothing!
                return this;
            int res = (currentSide == 0 ? player : npc).usar(it);
            if (res == 1) //item was used
                this.update();
            else
                dialog = new Dialog(500, t().INV_ERRORS[res + 3]);
        } else if (_name.equals(t().INV_MENUS_NAMES[0][2])) { //destroy
            if (it == null) //do nothing!
                return this;
            if (currentSide == 0) {
                if (player.getInventory().removeItem(it))
                    this.update();
            } else {
                if (npc != null)
                    npc.getInventory().removeItem(it, 1);
                else
                    container.getInventory().removeItem(it, 1);
                this.update();
            }
        } else if (_name.equals(t().INV_MENUS_NAMES[1][1]) || _name.equals(t().INV_MENUS_NAMES[1][2])) { //show/hide
            showCommerce = !showCommerce;
        } else if (_name.equals(t().INV_MENUS_NAMES[1][3]) || _name.equals(t().INV_MENUS_NAMES[1][4])) { //lock/unlock
            if (it == null) //do nothing!
                return this;
            ItemEx ex = itensAtuais[currentSide].get(selItem[currentSide]);
            ex.setLocked(!ex.isLocked());
        } else if (_name.equals(t().INV_MENUS_NAMES[1][5])) { //price
            if (it == null) //do nothing!
                return this;
            dialog = new Dialog(0, 500, "Choose the default selling price for this item. Don't raise it too much, though: nobody would buy it!", TipoInput.REAL_POSITIVO, LScript.format(itensAtuais[currentSide].get(selItem[currentSide]).getCost()), new String[]{"OK", "Cancel"});
        } else if (_name.equals(t().INV_MENUS_NAMES[3][1])) { //add
            if (it == null) //do nothing!
                return this;
            ItemEx ex = itensAtuais[currentSide].get(selItem[currentSide]);
            if (ex.getQtd() == 1) {
                if (currentSide == 0) {
                    if (player.getInventory().removeItem(it)) {
                        transaction.add(new TransItem(ex.getItem(), 1, false));
                        if (mode != 3)
                            transactionMoney += itensAtuais[currentSide].get(selItem[currentSide]).getCost();
                        this.update();
                    }
                } else {
                    if (npc != null) {
                        if (npc.getInventory().removeItem(it)) {
                            transaction.add(new TransItem(ex.getItem(), 1, true));
                            if (mode != 3)
                                transactionMoney -= itensAtuais[currentSide].get(selItem[currentSide]).getCost();
                            this.update();
                        }
                    } else {
                        if (container.getInventory().removeItem(it, 1)) {
                            transaction.add(new TransItem(ex.getItem(), 1, true));
                            this.update();
                        }
                    }
                }
            } else //show dialog for quantity
                dialog = new Dialog(1, 500, "How many items would you like to add?", TipoInput.NATURAL, String.valueOf(ex.getQtd()), new String[]{"OK", "Cancel"});
        } else if (_name.equals(t().INV_MENUS_NAMES[3][2])) { //add money
            dialog = new Dialog(2, 500, "How much money would you like to add? Remember, positive means you are receiving; negative means you are paying.", TipoInput.REAL, "", new String[]{"OK", "Cancel"});
        } else if (_name.equals(t().INV_MENUS_NAMES[3][3]) || _name.equals(t().INV_MENUS_NAMES[4][1])) { //commit OR accept
            int vt = validateTransaction();
            if (vt == 1)
                dialog = new Dialog(500, "You don't have enoguht inventory space to complete this transaction.");
            else if (vt == -1)
                dialog = new Dialog(500, "The other part doesn't have enought inventory space to complete this transaction.");
            else if (!(player.getInventory().canPay(-transactionMoney) && (npc == null ? container.getInventory().canPay(transactionMoney) : npc.getInventory().canPay(transactionMoney)))) //check if both parts have enough money to complete this transaction
                dialog = new Dialog(500, transactionMoney > 0 ? "The other part doesn't seem to have that money to pay you. Try loweing the transaction money." : "You don't have that money to pay! Try loweing the transaction money absolute value.");
            else if (mode == 3) { //you can do whatever you want
                player.getInventory().pay(-transactionMoney);
                if (npc != null)
                    npc.getInventory().pay(transactionMoney);
                else
                    container.getInventory().pay(transactionMoney);
                transactionMoney = 0;

                while (!transaction.isEmpty()) {
                    TransItem ti = transaction.remove(0);
                    if (ti.up)
                        player.getInventory().addItem(ti.getItem(), ti.getQtd());
                    else
                        if (npc != null)
                            npc.getInventory().addItem(ti.getItem(), ti.getQtd());
                        else
                            container.getInventory().addItem(ti.getItem(), ti.getQtd());
                }
                this.option = 0; //so that it doesn't select an inexisting item
                this.update();
            } else {
                boolean willAccept = lockedTransaction ? true : npc.wouldAccept(transaction, transactionMoney);
                if (willAccept) {
                    player.getInventory().pay(-transactionMoney);
                    npc.getInventory().pay(transactionMoney);
                    transactionMoney = 0;

                    while (!transaction.isEmpty()) {
                        TransItem ti = transaction.remove(0);
                        if (ti.up)
                            player.getInventory().addItem(ti.getItem(), ti.getQtd());
                        else
                            npc.getInventory().addItem(ti.getItem(), ti.getQtd());
                    }
                    
                    if (lockedTransaction)
                        return null; //finished
                    this.option = 0; //so that it doesn't select an inexisting item
                    this.update();
                } else
                    dialog = new Dialog(500, npc.getNome() + " didn't accept your offer. Try asking less, giving more or paying more.");
            }
        } else if (_name.equals(t().INV_MENUS_NAMES[3][4])) { //cancel
            this.cancelTransaction();
            this.update();
        } else if (_name.equals(t().INV_MENUS_NAMES[4][2])) { //change
            this.lockedTransaction = false;
        } else if (_name.equals(t().INV_MENUS_NAMES[4][3])) { //cancel
            this.cancelTransaction();
            return null;
        } else if (_name.charAt(0) == ':'){ //remove a given item
            TransItem ti = transaction.remove(Integer.parseInt(_name.substring(1)));
            if (ti.up) {
                if (npc != null)
                    npc.getInventory().addItem(ti.getItem(), ti.getQtd());
                else
                    container.getInventory().addItem(ti.getItem(), ti.getQtd());
            } else
                player.getInventory().addItem(ti.getItem(), ti.getQtd());
            this.update();
        }
        return this;
    }
    
    public void cancelTransaction() {
        transactionMoney = 0;
        
        //give items back
        while(!transaction.isEmpty()) {
            TransItem ti = transaction.remove(0);
            if (!ti.up)
                player.getInventory().addItem(ti.getItem(), ti.getQtd());
            else
                if (npc != null)
                    npc.getInventory().addItem(ti.getItem(), ti.getQtd());
                else
                    container.getInventory().addItem(ti.getItem(), ti.getQtd());
        }
        
        this.option = 0; //so that it doesn't select an inexisting item
    }

    @Override
    public int getActionCode() {
        return -1; //does everything inside
    }

    @Override
    public Menu back() {
        if (dialog != null) {
            dialog = null;
            return this;
        }
        this.dispose();
        return parent;
    }

    public void quickAccess(int code) {
        if (currentSide == 0 && profile != null)
            profile.getSave().setQuickAccess(code, getItem());
    }

    @Override
    public Menu setProfile(Profile profile) {
        this.profile = profile;
        if (mode == 0) { //displaying inventory
            player = profile.getSave().getPlayer(); //who knwos...
            update();
        }
        return this;
    }

    private void update() {
        gerarItensAtuais(0);
        if (mode != 0)
            gerarItensAtuais(1);

        //makes sure selector is in a existing place
        if (selItem[currentSide] != 0 && selItem[currentSide] > itensAtuais[currentSide].size() - 1)
            selItem[currentSide] = itensAtuais[currentSide].size() - 1;

        if (itensAtuais[currentSide].isEmpty())
            option = 0;
    }

    @Override
    public void dispose() {
        for (int i = 0; i < selItem.length; i++) {
            selItem[i] = 0;
            firstPos[i] = 0;
            selType[i] = 0;
        }
        currentSide = 0;
        
        this.cancelTransaction();
        transaction = new ArrayList<>();
        transactionMoney = 0;
    }

    @Override
    public Menu zerar() {
        this.dispose();
        return this;
    }

    @Override
    public void parse(int code, char c) {
        if (dialog != null) {
            dialog.parse(code, c);
            return;
        }
        Controls.Key key = Controls.get(code, true);
        if (key == SEL_UP)
            moveV(false);
        else if (key == SEL_DOWN)
            moveV(true);

        if (key == SEL_LEFT)
            moveH(false);
        else if (key == SEL_RIGHT)
            moveH(true);

        if (key == GO_UP)
            moveCV(false);
        else if (key == GO_DOWN)
            moveCV(true);

        if (key == ZOOM_IN)
            moveCH(true);
        else if (key == ZOOM_OUT)
            moveCH(false);

        if (key == QA_0)
            quickAccess(0);
        else if (key == QA_1)
            quickAccess(1);
        else if (key == QA_2)
            quickAccess(2);
        else if (key == QA_3)
            quickAccess(3);
    }
}
